﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceProcess;
using System.Text;
using System.Threading;

namespace nwrole.lib {
    public static class DeployUtil {
        #region Services

        private static string pluginServiceExe = "nwrole.pluginservice.exe";
        private static string supervisorServiceExe = "nwrole.supervisorservice.exe";

        public static string ServiceIdentifierFormat = "nwrole.integration.services.hosted.{0}";

        public static bool InstallSupervisor(string identifier)
        {
            string name = "nwrole Supervisor";

            StringBuilder b = new StringBuilder();
            b.AppendLine("Installing Supervisor: " + identifier);
            b.AppendLine("Name: " + name);
            //b.AppendLine("Username: " + serviceData.Username);
            //b.AppendLine("Password: " + serviceData.Password);  

            string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            bool result = ServiceUtil.InstallService(Path.Combine(appDir, pluginServiceExe), identifier, name, null, null);

            b.AppendLine("Result: " + result);

            EventLogger.LogInformation(b.ToString());

            return true;
        }

        public static bool InstallService(ServiceData serviceData, int supervisorPort, string pluginProviderClassName) {
            if (ServiceInstalled(serviceData.Id))
                return true;

            StringBuilder b = new StringBuilder();
            b.AppendLine("Installing Service: " + serviceData.Id);
            b.AppendLine("Name: " + serviceData.Name);
            b.AppendLine("ManagementPort: " + serviceData.ManagementPort);
            b.AppendLine("ServiceState: " + serviceData.ServiceState);
            b.AppendLine("Deployed: " + serviceData.Deployed);
            //b.AppendLine("DisplayName: " + serviceData.DisplayName);
            b.AppendLine("Username: " + serviceData.Username);
            b.AppendLine("Password: " + serviceData.Password);  

            EventLogger.LogInformation("DeployUtil.InstallService", String.Format("Installing service '{0}'.", serviceData.Id));
            /*serviceData.ManagementPort = 0;
            serviceData.ManagementPort = NextFreeManagementPort();*/
            string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            b.AppendLine("===");
            b.AppendLine("Service path: " + Path.Combine(appDir, pluginServiceExe) + " \"" + pluginProviderClassName + "\" " + serviceData.Id.ToString("N") + " " + serviceData.Name + " " + serviceData.ManagementPort.ToString() + " " + supervisorPort);
            b.AppendLine("Service identifier: " + ServiceIdentifier(serviceData.Id));
           var result = ServiceUtil.InstallService(Path.Combine(appDir, pluginServiceExe) + " \"" + pluginProviderClassName + "\" " + serviceData.Id.ToString("N") + " " + serviceData.Name + " "+ serviceData.ManagementPort.ToString() + " " + supervisorPort,
                ServiceIdentifier(serviceData.Id),
                serviceData.Name,
                serviceData.Username,
                serviceData.Password);

           b.AppendLine("Result: " + result);
          EventLogger.LogInformation(b.ToString());
            EventLogger.LogInformation("DeployUtil.InstallService", String.Format("Installed service '{0}'.", serviceData.Id));
            return true;
        }

        public static bool UninstallService(Guid serviceId) {
            if (!ServiceInstalled(serviceId))
                return true;
            EventLogger.LogInformation("DeployUtil.UninstallService", String.Format("Uninstalling service '{0}'.", serviceId));
            ServiceUtil.UninstallService(ServiceIdentifier(serviceId));
            EventLogger.LogInformation("DeployUtil.UninstallService", String.Format("Uninstalled service '{0}'.", serviceId));
            return true;
        }

        public static bool StartService(Guid serviceId) {
          EventLogger.LogInformation(DateTime.Now.ToString("HH:mm:ss") + " - Starting service");
          EventLogger.LogInformation(DateTime.Now.ToString("HH:mm:ss") + " - (Installed) " + ServiceInstalled(serviceId));
          EventLogger.LogInformation(DateTime.Now.ToString("HH:mm:ss") + " - (Running) " + ServiceRunning(serviceId));
            if (!ServiceInstalled(serviceId) || ServiceRunning(serviceId))
                return false;
            ServiceController ctrl = new ServiceController(ServiceIdentifier(serviceId));
            try {
                ctrl.Start();
                ctrl.WaitForStatus(ServiceControllerStatus.Running);
                EventLogger.LogInformation(DateTime.Now.ToString("HH:mm:ss") + " - Started " + ctrl.Status.ToString());
            } catch (Exception exception) {
                EventLogger.LogException("DeployUtil.StartService", exception);
                return false;
            } finally { ctrl.Dispose(); }
            return true;
        }

        public static bool StopService(Guid serviceId) {
            if (!ServiceInstalled(serviceId) || !ServiceRunning(serviceId))
                return false;
            ServiceController ctrl = new ServiceController(ServiceIdentifier(serviceId));
            try {
                ctrl.Stop();
                ctrl.WaitForStatus(ServiceControllerStatus.Stopped);
            } catch (Exception exception) {
                EventLogger.LogException("DeployUtil.StopService", exception);
                return false;
            } finally { ctrl.Dispose(); }
            return true;
        }

        public static bool ServiceInstalled(Guid serviceId) {
            ServiceController ctrl = new ServiceController(ServiceIdentifier(serviceId));
            try { var status = ctrl.Status; } catch {
                return false;
            } finally { ctrl.Dispose(); }
            return true;
        }

        public static bool ServiceRunning(Guid serviceId) {
            if (!ServiceInstalled(serviceId))
                return false;
            ServiceController ctrl = new ServiceController(ServiceIdentifier(serviceId));
            try { return (ctrl.Status == ServiceControllerStatus.Running); } catch (Exception exception) {
                EventLogger.LogException("DeployUtil.ServiceInstalled", exception);
                return false;
            } finally { ctrl.Dispose(); }
        }

        /*public static bool ServiceManagerRunning(nwrole.lib.Solution.Service service) {
            ServiceManagement mgr = null;
            try {
                mgr = new ServiceManagement(service);
                if (mgr.GetIdentifier().ToString("N") != service.Id.ToString("N"))
                    return false;
            } catch (Exception exception) {
                EventLogger.LogException("DeployUtil.ServiceManagerRunning", exception);
                return false;
            } finally {
                if (mgr != null)
                    mgr.Dispose();
            }
            return true;
        }*/

        public static string ServiceIdentifier(Guid id) {
            return String.Format(ServiceIdentifierFormat, id.ToString("N"));
        }

        #endregion
        #region Solutions
        public static Result DeployService(ServiceData serviceData, bool forceDeployment, int supervisorPort, string pluginProviderClassName) {
            //ServiceData serviceData = Configuration.ServiceDataWithId(serviceId);
            
            if (serviceData == null) {
                EventLogger.LogError("DeployUtil.DeployService", String.Format("Service '{0}' does not exist.", serviceData));
                return Result.SolutionIdUnavailable;
            }

            if (!forceDeployment && serviceData.Deployed) {
                EventLogger.LogError("DeployUtil.DeployService", String.Format("Service '{0}' is already deployed", serviceData));
                return Result.SolutionAlreadyDeployed;
            }

            if (DeployUtil.ServiceInstalled(serviceData.Id)) {
                if (DeployUtil.ServiceRunning(serviceData.Id))
                    DeployUtil.StopService(serviceData.Id);
                DeployUtil.UninstallService(serviceData.Id);
                Thread.Sleep(2000); // Give it some time to shut down all threads etc. WCF services needs this.
            }

            if (DeployUtil.InstallService(serviceData, supervisorPort, pluginProviderClassName)) {
                serviceData.ServiceState = ServiceState.Stopped;
                serviceData.DeployedDate = DateTime.Now;
                serviceData.Deployed = true;

                if (DeployUtil.StartService(serviceData.Id)) {
                    serviceData.ServiceState = ServiceState.Running;

                    /*ServiceChannel channel = new ServiceChannel(serviceData);
                    channel.Start();
                    channel.Dispose();*/
                }
            }
            
            //Configuration.Save();

            return Result.Success;
        }

        public static Result RetractService(ServiceData serviceData, bool forceRetraction) {
            //ServiceData serviceData = Configuration.ServiceDataWithId(serviceId);

            /*if (serviceData == null) {
                EventLogger.LogError("DeployUtil.RetractService", String.Format("Service '{0}' does not exist.", serviceId));
                return Result.SolutionIdUnavailable;
            }*/

            if (!serviceData.Deployed && !forceRetraction) {
                EventLogger.LogError("DeployUtil.RetractService", String.Format("Service '{0}' is already retracted", serviceData.Id));
                return Result.SolutionAlreadyRetracted;
            }

            if (serviceData.ServiceState == ServiceState.Running) {
                DeployUtil.StopService(serviceData.Id);
                serviceData.ServiceState = ServiceState.Stopped;
            }
            if (serviceData.ServiceState != ServiceState.Deactivated) {
                DeployUtil.UninstallService(serviceData.Id);
                serviceData.ServiceState = ServiceState.Deactivated;
            }

            serviceData.Deployed = false;
            serviceData.DeployedDate = null;

            //Configuration.Save();

            return Result.Success;
        }
        #endregion
    }
}
